package com.ruoyi.framework.interceptor;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.framework.interceptor.anno.MyPermissions;

import lombok.extern.slf4j.Slf4j;

/**
 * 自定义拦截器
 * 自定义拦截器后，需要配置进Spring
 *
 * 拦截器Interceptor可以拿到原始的HTTP请求和响应的信息，
 *    也可以拿到你真正处理请求方法的信息，但是拿不到传进参数的那个值。
 *
 *拦截顺序：filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller
 */
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {
 
    /**
     * 在访问Controller某个方法之前这个方法会被调用。
     * @param request
     * @param response
     * @param handler
     * @return false则表示不执行postHandle方法,true 表示执行postHandle方法
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    	SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal(); // 获取当前登录用户
    	if (user != null) {
    		log.info("Token Interceptor preHandle {}", user.getUserName());
    	} else {
    		log.info("Token Interceptor preHandle {}", "annoymus");
    	}
    	//log.info("Token Interceptor preHandle {}","");
        //String token = request.getHeader("token");
        //log.info("Token Interceptor preHandle token :{}",token);
        //log.info("Token Interceptor preHandle uri {}",request.getRequestURL().toString());

        //spring boot 2.0对静态资源也进行了拦截，当拦截器拦截到请求之后，
        // 但controller里并没有对应的请求时，该请求会被当成是对静态资源的请求。
        // 此时的handler就是 ResourceHttpRequestHandler，就会抛出上述错误。
        if (handler instanceof HandlerMethod){
        	String rst = request.getRequestURL().toString();//getUrl(request, handler);
        	log.info(rst);
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            //log.info("Token Interceptor preHandle getMethod {}", method.getName());
            
            //获取类上的注解
            MyPermissions annoClz = handlerMethod.getBeanType().getAnnotation(MyPermissions.class);
            if (annoClz != null) {
        		String clzFuncDesc = !annoClz.value().isEmpty() ? annoClz.value() : "功能";
            	String clzFuncName = getAnnoPrefix(handlerMethod.getBeanType());
            	//获取方法上的注解 方式2
                MyPermissions annoMethod = handlerMethod.getMethod().getAnnotation(MyPermissions.class);
                if (annoMethod != null) {
                	String methodFuncDesc = !annoMethod.value().isEmpty() ? annoMethod.value() : "操作";
                	String methodFuncName = getMethodFunc(handlerMethod);
                    log.info("Token Interceptor preHandle getMethod {}, {}, {}, {}, {}", 
                    		method.getName(), clzFuncName, methodFuncName, clzFuncDesc, methodFuncDesc);
                    if (!isPermitted(clzFuncName, methodFuncName)) {
                    	String msg = String.format("当前用户无权限操作: %s-%s", clzFuncDesc, methodFuncDesc);
                    	throw new ServiceException(msg);
//                        if (ServletUtils.isAjaxRequest(request)) {
//                        	AjaxResult result = AjaxResult.error(msg);
//                        	outText(response, result);
//                        }  else {
//                        	HttpSession session = request.getSession(true);
//                        	session.setAttribute("errorMessage", msg);
//                        	response.sendRedirect("/error/service.html");
//                        	//request.getRequestDispatcher("/error/service.html").forward(request,response);
//                        	//response.sendRedirect(request.getContextPath() + "/error/service.html");
//                            //return new ModelAndView("error/service", "errorMessage", e.getMessage());
//                        }
                        //return false;
                    }

            		//tryPermitted(clzFuncName, methodFuncName, clzFuncDesc, methodFuncDesc);
                }
            }
        } else if(handler instanceof ResourceHttpRequestHandler){//静态资源
            ResourceHttpRequestHandler resourceHttpRequestHandler = (ResourceHttpRequestHandler) handler;
            log.info("Token Interceptor preHandle getMethod {}",resourceHttpRequestHandler.getMediaTypes());
        }
 
        //false则表示不执行postHandle方法,不执行下一步chain链，直接返回response
        return true;
    }
    
    public String getUrl(HttpServletRequest request, Object handler) {
        StringBuilder sb = new StringBuilder();
//        String url =request.getRequestURL().toString();  
//		   if(!url.contains(".css") && !url.contains(".js") && !url.contains(".png") &&  !url.contains(".jpg")) { 
//			   System.out.println("*******************访问地址："+url+"*******************");
//			   System.out.println("*******************客户端请求地址和操作端口：" + request.getRemoteAddr() + ":" + request.getRemotePort() + "*******************");
//		   }
 
        //获取请求参数
        Enumeration<String> em = request.getParameterNames();
        //JSONObject data = new JSONObject();
        Map<String, Object> data = new HashMap<>();
        while (em.hasMoreElements()) {
            String name = (String) em.nextElement();
            String value = request.getParameter(name);
            data.put(name,value);
        }
        sb .append("-------------------------------------------------------------\n");
        HandlerMethod h = (HandlerMethod) handler;
        sb.append("Controller: ").append(h.getBean().getClass().getName()).append("\n");
        sb.append("Method    : ").append(h.getMethod().getName()).append("\n");
        sb.append("Params    : ").append(data).append("\n");
        sb.append("URI       : ").append(request.getRequestURI()).append("\n");
        sb.append("URL       : ").append(request.getRequestURL()).append("\n");
        sb .append("-------------------------------------------------------------\n");

        String rst = sb.toString();
        System.out.println(rst);
 
        return  rst;
    }
    
    public void outText(HttpServletResponse response, Object obj) throws IOException {
    	outText(response, JSON.toJSON(obj));
    }
    
    public void outText(HttpServletResponse response, String text) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().println(text);
        return;
    }
    
    public String getMethodFunc(HandlerMethod handlerMethod) {
    	String[] arMethod = null;
		RequestMapping annoReqMethod = handlerMethod.getMethod().getAnnotation(RequestMapping.class);
		if (annoReqMethod != null) {
    		arMethod = annoReqMethod.value();
		} else {
			GetMapping annoGetMethod = handlerMethod.getMethod().getAnnotation(GetMapping.class);
    		if (annoGetMethod != null) {
        		arMethod = annoGetMethod.value();
    		} else {
    			PostMapping annoPostMethod = handlerMethod.getMethod().getAnnotation(PostMapping.class);
        		if (annoPostMethod != null) {
            		arMethod = annoPostMethod.value();
        		}
    		}
		}

		String methodReqMap = (arMethod != null && arMethod.length >0) ? arMethod[0] : "";
		return methodReqMap;
    }
    
	public String getAnnoPrefix(Class<?> clz) {
		RequestMapping annotation = clz.getAnnotation(RequestMapping.class);
		String[] ar = annotation.value();
		return ar.length >0 ? ar[0] : "";
	}
	
	public String getName(RequestMapping annotation) {
		String[] ar = annotation.value();
		return ar.length >0 ? ar[0] : "";
	}
    
	public String getAnnoPrefix() {
		return getAnnoPrefix(this.getClass());
	}
	
	public boolean isPermitted(String clz, String method) throws Exception {
		String url = clz + "/" + method;
		return isPermitted(url);
	}
	
	public boolean isPermitted(String url) throws Exception {
		Subject subject = SecurityUtils.getSubject();
		if (subject.isPermitted(url)){
			return true;
		} else {
			return false;
		}
	}
	
//	public void tryPermitted(String clz, String method, String msg) throws Exception {
//		if (!this.isPermitted(clz, method)){
//			throw new ServiceException("当前用户无权限操作: " + msg);
//		}
//	}
//	
//	public void tryPermitted(String clz, String method, String clzFunc, String methodFunc) throws Exception {
//		if (this.isPermitted(clz, method)){
//			String msg = String.format("当前用户无权限操作: %s-%s", clzFunc, methodFunc);
//			throw new ServiceException(msg);
//			//throw new Exception("当前用户无权限操作: " + msg);
//		}
//	}
	
    /**
     * 判断是否是Ajax请求
     *
     * @param request
     * @return
     */
    public boolean isAjax(HttpServletRequest request) {
        return (request.getHeader("X-Requested-With") != null &&
                "XMLHttpRequest".equals(request.getHeader("X-Requested-With").toString()));
    }

    /**
     * 请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后）
     * preHandle方法处理之后这个方法会被调用，如果控制器Controller出现了异常，则不会执行此方法
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("Token Interceptor postHandle");  
    }
 
    /**
     * 不管有没有异常，这个afterCompletion都会被调用
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("Token Interceptor afterCompletion");
    }
 
}